package com.itheima.utils;

import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.common.Trie;
import com.itheima.entity.SensitiveVocabulary;
import com.itheima.enums.RedisEnum;
import com.itheima.service.SensitiveVocabularyService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.CharUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <h1>过滤敏感信息工具类</h1>
 *
 * @author <a href="515857087@qq.com">叶世铭</a>
 * @since 2022/12/07
 */
@Component
@Slf4j
public class SensitiveWordFilterUtil {

    @Resource
    private SensitiveVocabularyService sensitiveVocabularyService;

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 用来替代敏感词
     */
    private static final String REPLACEMENT = "**";

    /**
     * 前缀树的根节点
     */
    private static Trie rootNode = new Trie();

    @PostConstruct
    public void init() throws JsonProcessingException {
        Object o = redisTemplate.opsForValue().get(RedisEnum.SENSITIVE_VOCABULARY_TRIE.getRedis());
        if (StringUtils.isEmpty(o)) {
            createTrie();
            insertRedis();
        } else {
            String sensitiveVocabulary = o.toString();
            rootNode = JSONObject.parseObject(sensitiveVocabulary, Trie.class);
        }
    }

    public void createTrie() throws JsonProcessingException {
        List<SensitiveVocabulary> list = sensitiveVocabularyService.lambdaQuery()
                .select(SensitiveVocabulary::getWord)
                .list();
        List<String> collect = list.stream()
                .map(SensitiveVocabulary::getWord)
                .collect(Collectors.toList());
        collect.forEach(this::addWord);
        insertRedis();

    }

    /**
     * 把敏感词前缀树加入到redis中
     * @throws JsonProcessingException
     */
    public void insertRedis() throws JsonProcessingException {
        //使用jackson，把对象转为字符串
        ObjectMapper objectMapper = new ObjectMapper();
        redisTemplate.opsForValue().set(RedisEnum.SENSITIVE_VOCABULARY_TRIE.getRedis(), objectMapper.writeValueAsString(rootNode));
    }

    /**
     * 添加敏感词到前缀树
     *
     * @param word
     */
    public void addWord(String word) {
        Trie node = rootNode;
        for (int i = 0; i < word.length(); i++) {
            if (!node.getSubNode().containsKey(word.charAt(i))) {
                node.addSubNode(word.charAt(i), new Trie());
            }
            node = node.getSubNode().get(word.charAt(i));
            if (i == word.length() - 1) {
                node.setEnd(true);
            }
        }
    }

    /**
     * 敏感词用**替换
     * @param word 内容
     * @return 剔除敏感词后的内容
     * @throws JsonProcessingException
     */
    public String filter(String word) throws JsonProcessingException {
        Object o = redisTemplate.opsForValue().get(RedisEnum.SENSITIVE_VOCABULARY_TRIE.getRedis());
        if (StringUtils.isEmpty(o)) {
            createTrie();
            insertRedis();
        }
        Trie temp = rootNode;
        int low = 0;
        int fast = 0;

        StringBuilder res = new StringBuilder();

        while (fast < word.length()) {
            char mid = word.charAt(fast);
            if (isSymbol(mid)) {
                if (temp == rootNode) {
                    res.append(mid);
                    low++;
                }
                fast++;
                continue;
            }
            if (!temp.getSubNode().containsKey(mid)) {
                res.append(word.charAt(low));
                low++;
                fast = low;
                temp = rootNode;
            } else if (temp.getSubNode().get(mid).isEnd()) {
                res.append(REPLACEMENT);
                fast++;
                low = fast;
                temp = rootNode;
            } else {
                fast++;
                temp = temp.getSubNode().get(mid);
            }

        }
        res.append(word.substring(low));
        return res.toString();
    }

    /**
     * 是符号就返回true
     *
     * @param c
     * @return
     */
    public boolean isSymbol(Character c) {
        //CharUtils.isAsciiAlphanumeric(c)判断是不是合法字符，是的话返回true
        //c < 0x2E80 || c > 0x9fff 东亚文字的范围是0x2E80到0x9fff
        return CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9fff);

    }

}
